zk.afterLoad('zul.menu', function () {
    var _menupopup = {}
    _menupopupMolds = {};

    zk.override(zul.menu.Menupopup.molds, _menupopupMolds, {
        'bs': zul.menu.Menupopup.molds['default']
    });

    zk.override(zul.menu.Menupopup.prototype, _menupopup, {
        _inBSMold: function () {
            return this._mold == 'bs';
        },
        redraw: function (out) {
            if (this._inBSMold()) {
                var uuid = this.uuid,
                    tags = zk.ie < 11 || zk.gecko ? 'a' : 'button';
                out.push('<div', this.domAttrs_(), '><', tags, ' id="', uuid,
                    '-a" tabindex="-1" onclick="return false;" href="javascript:;"',
                    ' class="z-focus-a"></',
                    tags, '><div class="', this.$s('separator'), '"></div><div class="', this.$s('content'), '" id="', uuid, '-cave">');

                for (var w = this.firstChild; w; w = w.nextSibling)
                    w.redraw(out);

                out.push('</div></div>');
            } else
                _menupopup.redraw.apply(this, arguments);
        },
        getZclass: function () {
            if (this._inBSMold()) {
                return this._zclass ? this._zclass : '';
            } else
                return _menupopup.getZclass.apply(this, arguments);
        },
        $s: function (subclass) {
            if (this._inBSMold()) {
                switch (subclass) {
                    case 'separator':
                        return '';
                    case 'content':
                        return 'dropdown-menu';
                }
                return '';
            } else
                return _menupopup.$s.apply(this, arguments);
        },
        open: function () {
            if (this._inBSMold()) {
                this.forcerender();
                jq(this.$n('cave')).css({position: 'relative', display: 'block'});
                _menupopup.open.apply(this, arguments);
            } else
                return _menupopup.open.apply(this, arguments);
        }
    });

    var _menuseparator = {};

    zk.override(zul.menu.Menuseparator.prototype, _menuseparator, {
        _inBSMold: function () {
            return this.parent && this.parent._inBSMold && this.parent._inBSMold();
        },
        redraw: function (out) {
            if (this._inBSMold()) {
                out.push('<div', this.domAttrs_(), '></div>');
            } else
                _menuseparator.redraw.apply(this, arguments);
        },
        getZclass: function () {
            if (this._inBSMold())
                return this._zclass != null ? this._zclass : 'dropdown-divider';
            return _menuseparator.getZclass.apply(this, arguments);
        }
    });

    var _menuitem = {};

    zk.override(zul.menu.Menuitem.prototype, _menuitem, {
        _inBSMold: function () {
            return this.parent && this.parent._inBSMold && this.parent._inBSMold();
        },
        domContent_: function () {
            var label = '<span class="' + this.$s('text') + '">'
                + (zUtl.encodeXML(this.getLabel())) + '</span>',
                icon = '<i class="' + this.$s('icon') + ' z-icon-check"></i>',
                img = this.getImage(),
                iconSclass = this.domIcon_();

            if (img)
                img = '<img src="' + img + '" class="' + this.$s('image') + '" align="absmiddle" />'
                    + (iconSclass ? ' ' + iconSclass : '');
            else {
                if (iconSclass) {
                    img = iconSclass;
                } else {
                    img = '<img ' + (this.isTopmost() ? 'style="display:none"' : '')
                        + ' src="" class="' + this.$s('image') + '" align="absmiddle" />';
                }
            }

            return this._inBSMold() ? img + label + (this.isAutocheck() || this.isCheckmark() ? icon : '') : img + (this.isAutocheck() || this.isCheckmark() ? icon : '') + label;
        },
        doClick_: function (evt) {
            if (this._disabled)
                evt.stop();
            else {
                if (!this._canActivate(evt)) return;
                if (!this._upload)
                    zul.wgt.ADBS.autodisable(this);
                else if (!zk.ie || zk.ie > 10) {// ZK-2471
                    if (!zk.chrome || evt.domTarget.type != 'file') //ZK-3089
                        this._uplder.openFileDialog();
                }

                var topmost = this.isTopmost(),
                    anc = this._inBSMold() ? this.$n() : this.$n('a');

                if (anc.href.startsWith('javascript:')) {
                    if (this.isAutocheck()) {
                        this.setChecked(!this.isChecked());
                        this.fire('onCheck', this.isChecked());
                    }
                    this.fireX(evt);
                    //ZK-2679: prevent default behavior when upload=false
                    if (!this._upload) //if upload=true, it won't fire onbeforeunload in IE <= 10
                        evt.stop(); //if we stop evt when upload=true, it won't open upload window in IE <= 10
                } else if (anc.href.toLowerCase().startsWith('mailto:')) { // ZK-2506
                    var ifrm = jq.newFrame('mailtoFrame', anc.href, null);
                    jq(ifrm).remove();
                    evt.stop();
                } else {
                    if (zk.ie < 11 && topmost && this.$n().id != anc.id)
                        zUtl.go(anc.href, {target: anc.target});
                    // Bug #1886352 and #2154611
                    //Note: we cannot eat onclick. or, <a> won't work

                    if (zk.gecko && topmost && this.$n().id != anc.id) {
                        zUtl.go(anc.href, {target: anc.target});
                        evt.stop();
                        // Bug #2154611 we shall eat the onclick event, if it is FF3.
                    }
                }
                if (!topmost) {
                    for (var p = this.parent; p; p = p.parent) {
                        if (p.$instanceof(zul.menu.Menupopup)) {
                            // if close the popup before choosing a file, the file chooser can't be triggered.
                            if (!p.isOpen() || this._uplder || p._keepOpen /*Bug #2911385 && !this._popup*/)
                                break;
                            this._updateHoverImage(); // remove hover image
                            p.close({sendOnOpen: true});
                        } else if (!p.$instanceof(zul.menu.Menu)) //either menubar or non-menu*
                            break;
                        else
                            p._updateHoverImage(); // remove parent Menu hover image
                    }
                }

                var menubar;
                if (zk.webkit && (menubar = this.getMenubar()) && menubar._autodrop)
                    menubar._noFloatUp = true;
                //_noFloatUp used in Menu.js to fix Bug 1852304
                this.$super('doClick_', evt, true);
            }
        },
        redraw: function (out) {
            if (this._inBSMold()) {
                var uuid = this.uuid,
                    target = this.getTarget();
                out.push('<a ', this.domAttrs_(), 'href="', this.getHref() ? this.getHref() : 'javascript:;', '"');
                if (target)
                    out.push(' target="', target, '"');
                out.push(' id="', uuid, '-a" class="', this.$s('content'), '"',
                    this._disabled ? ' disabled="disabled"' : '',
                    '>', this.domContent_(), '</a>'); //Merge breeze
            } else
                _menuitem.redraw.apply(this, arguments);
        },
        getZclass: function () {
            if (this._inBSMold())
                return this._zclass != null ? this._zclass : 'dropdown-item';
            return _menuitem.getZclass.apply(this, arguments);
        },
        getImageNode: function () {
            if (this._inBSMold()) {
                if (!this._eimg && (this._image || this._hoverImage)) {
                    var n = this.$n();
                    if (n)
                        this._eimg = this.$n().firstChild;
                }
                return this._eimg;
            } else
                _menuitem.getImageNode.apply(this, arguments);
        }
    });

});